use clap::{Parser, Subcommand};
use encoding_rs::{Encoding, UTF_8};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::{env, fs};

lazy_static! {
    pub static ref CONFIG: Config = get_config();
    pub static ref REGISTRY: Registry = get_registry();
    pub static ref CLI: Cli = get_cli();
}

pub static DEFAULT_PORT: u16 = 6969;
pub static DEFAULT_SERVER_ID: usize = 1;
pub static DEFAULT_WORKER: usize = 2;

// 1 min
pub static DEFAULT_CLIENT_CONNECT_RETRY_WAIT: usize = 60;
// 1 secs
pub static MIN_CLIENT_CONNECT_RETRY_WAIT: usize = 1;
// 1 days
pub static MAX_CLIENT_CONNECT_RETRY_WAIT: usize = 24 * 60 * 60;

// 3 secs
pub static DEFAULT_SERVER_HEARTBEAT_INTERVAL: usize = 3;
// 10 secs
pub static DEFAULT_SERVER_HEARTBEAT_TIMEOUT: usize = 10;

pub static DEFAULT_PID_FILE: &str = concat!(env!("CARGO_PKG_NAME"), ".pid");

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
    // 服务端
    pub server: Option<ConfigServer>,
    // 客户端
    pub client: Option<ConfigClient>,
    // 集群服务节点
    pub member: Option<HashMap<String, ConfigNode>>,
}

impl Config {
    pub fn new() -> Self {
        let heartbeat = ConfigHeartbeat {
            interval: Some(DEFAULT_SERVER_HEARTBEAT_INTERVAL),
            timeout: Some(DEFAULT_SERVER_HEARTBEAT_TIMEOUT),
        };

        let server = ConfigServer {
            server_id: Some(DEFAULT_SERVER_ID),
            port: Some(DEFAULT_PORT),
            worker: Some(DEFAULT_WORKER),
            auto_switchover: Some(false),
            data_root: None,
            singleton: Some(false),
            heartbeat: Some(heartbeat),
        };

        let client = ConfigClient {
            // singleton: Some(false),
            app_key: Some(String::new()),
            app_secret: Some(String::new()),
            connect_retry_wait: Some(DEFAULT_CLIENT_CONNECT_RETRY_WAIT),
        };

        let members = HashMap::new();
        // members.insert(
        //     "gg".to_owned(),
        //     ConfigNode {
        //         id: SERVER_ID,
        //         host: Some("172.17.0.2".to_owned()),
        //         port: Some(DEFAULT_PORT),
        //         weight: Some(1),
        //     },
        // );

        // members.insert(
        //     "zy".to_owned(),
        //     ConfigNode {
        //         id: SERVER_ID + 1,
        //         host: Some("172.17.0.3".to_owned()),
        //         port: Some(DEFAULT_PORT),
        //         weight: Some(1),
        //     },
        // );

        // members.insert(
        //     "zz".to_owned(),
        //     ConfigNode {
        //         id: SERVER_ID + 2,
        //         host: Some("172.17.0.4".to_owned()),
        //         port: Some(DEFAULT_PORT),
        //         weight: Some(0),
        //     },
        // );

        Config {
            server: Some(server),
            client: Some(client),
            member: Some(members),
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ConfigServer {
    // ID：默认为1
    pub server_id: Option<usize>,
    // 启动端口：默认为6969
    pub port: Option<u16>,
    // 启动线程数：默认为2
    pub worker: Option<usize>,
    // 故障恢复后自动切换为leader
    pub auto_switchover: Option<bool>,
    // 是否为单机模式
    pub singleton: Option<bool>,
    // 数据文件目录
    pub data_root: Option<String>,
    // 心跳信息
    pub heartbeat: Option<ConfigHeartbeat>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ConfigHeartbeat {
    // 心跳间隔，秒
    pub interval: Option<usize>,
    // 心跳超时，秒
    pub timeout: Option<usize>,
}

///
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ConfigClient {
    // 是否跟随server启动，默认为false
    // pub singleton: Option<bool>,
    // APP_KEY
    pub app_key: Option<String>,
    // APP_SECRET
    pub app_secret: Option<String>,
    // 连接丢失的情况下 重新尝试连接主服务器之前睡眠的秒数
    pub connect_retry_wait: Option<usize>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ConfigNode {
    // ID，默认为1
    pub id: usize,
    // 服务节点，默认127.0.0.1
    pub host: Option<String>,
    // 服务端口，默认6969
    pub port: Option<u16>,
    // 权重：默认为1
    pub weight: Option<usize>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Registry {
    // 环境变量
    pub env: Option<HashMap<String, String>>,
    // pub items: HashMap<String, RegistryService>,
    pub services: HashMap<String, RegistryServiceEntry>,
    // pub services: HashMap<String, RegistryService>,
}

impl Registry {
    pub fn new() -> Self {
        Registry {
            env: None,
            services: HashMap::new(),
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RegistryServiceEntry {
    // 编码。默认为utf8
    // pub encoding: Option<String>,
    // 环境变量
    pub env: Option<HashMap<String, String>>,
    // 所在目录
    pub root: Option<String>,

    // 检查间隔，默认10秒
    pub check_interval_secs: Option<usize>,
    // 检查脚本
    pub check_cmd: Vec<String>,
    pub start_cmd: Vec<String>,
    pub stop_cmd: Vec<String>,

    // 切换到leader后需要执行的脚本，停止程序、重置、写断点文件、启动程序
    pub to_leader_cmd: Vec<String>,
    // 切换到follower后需要执行的脚本，停止程序、重置
    pub to_follower_cmd: Vec<String>,

    // 是否使用广播
    pub enable_broadcast: Option<bool>,
    // 广播实体
    pub broadcast: Option<RegistryBroadcastEntry>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RegistryBroadcastEntry {
    // 所在目录
    pub root: Option<String>,
    // 广播脚本
    pub cmd: Vec<String>,
    // 广播间隔，默认为3
    pub interval_secs: Option<usize>,
    // 输入文件，无默认，配置后会读取改文件
    // 支持如下格式：
    // 本地：
    // 1）/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 2）file:///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 远程：
    // 1）ayisha://192.168.1.1,192.168.1.2,192.168.1.3/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 2）ayisha://192.168.1.1,192.168.1.2,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    pub input: Option<String>,
    // 输出文件，无默认
    // 支持如下格式：
    // 本地：
    // 1）/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 2）file:///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 远程：
    // 1）ayisha://192.168.1.1,192.168.1.2,192.168.1.3/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    // 2）ayisha://192.168.1.1,192.168.1.2,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties
    pub output: Option<String>,
    // 广播变量，默认全部广播
    pub props: Vec<String>,
}

/// Host Based Authorization - Ident
#[derive(Debug, Clone, Deserialize)]
pub struct HostBasedAuth {
    pub map: HashMap<String, Vec<Ident>>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Ident {
    #[allow(dead_code)]
    pub user: String,
    pub app_key: String,
    pub app_secret: String,
}

impl HostBasedAuth {
    pub fn new() -> Self {
        if let Some(cmd) = get_cli_command() {
            match cmd {
                Commands::Server {
                    config_file: _,
                    log4rs_file: _,
                    server_id: _,
                    server_port: _,
                    registry_file: _,
                    hba_ident_file,
                    pid_file: _,
                } => {
                    if let Some(hba_ident_file) = hba_ident_file {
                        let toml_text = fs::read_to_string(&hba_ident_file).unwrap();
                        let hba: HostBasedAuth = match toml::from_str(&toml_text) {
                            Ok(p) => p,
                            Err(e) => {
                                panic!(
                                    "<ParseTOML> File {} cannot be parsed, cause:\n{}",
                                    hba_ident_file.display(),
                                    e
                                );
                            }
                        };
                        return hba;
                    }
                }
                _ => {}
            }
        }

        HostBasedAuth {
            map: HashMap::new(),
        }
    }

    pub fn match_ident(&self, app_key: &str, app_secret: &str) -> HashMap<String, Ident> {
        let mut ret = HashMap::new();

        for (host, idents) in self.map.iter() {
            for ident in idents {
                if ident.app_key.eq_ignore_ascii_case(app_key)
                    && ident.app_secret.eq_ignore_ascii_case(app_secret)
                {
                    ret.insert(host.to_owned(), ident.to_owned());
                }
            }
        }

        ret
    }
}

// https://docs.rs/clap/latest/clap/_derive/index.html
#[derive(Parser, Clone, Debug)]
#[command(about, long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand, Clone, Debug)]
pub enum Commands {
    /// Starts the ayisha server.
    Server {
        /// Only read default options from the given file
        #[arg(
            short = 'c',
            long,
            env = "AYISHA_CONF_FILE",
            default_value = "ayisha.toml",
            value_name = "FILE"
        )]
        config_file: Option<PathBuf>,

        /// Only read logging format options from the given file
        #[arg(
            short = 'l',
            long,
            env = "AYISHA_LOG4RS_FILE",
            default_value = "log4rs.toml",
            value_name = "FILE"
        )]
        log4rs_file: Option<PathBuf>,

        /// Uniquely identifies the server instance in the cluster, built-in default 1
        #[arg(short = 'i', long, env = "AYISHA_SERVER_ID", value_name = "#")]
        server_id: Option<usize>,

        /// Port number to use for connection, $AYISHA_SERVER_PORT, ayisha.toml, built-in default 6969, whatever comes first
        #[arg(short = 'p', long, env = "AYISHA_SERVER_PORT", value_name = "#")]
        server_port: Option<u16>,

        /// Only read service options from the given file
        #[arg(
            short = 'r',
            long,
            env = "AYISHA_REGISTRY_FILE",
            default_value = "ayisha-registry.toml",
            value_name = "FILE"
        )]
        registry_file: Option<PathBuf>,

        /// Only read Host Based Authorization - Ident options from the given file
        #[arg(
            short = 'H',
            long,
            env = "AYISHA_HBA_IDENT_FILE",
            default_value = "hba_ident.toml",
            value_name = "FILE"
        )]
        hba_ident_file: Option<PathBuf>,

        /// Pid file used by systemctl
        #[arg(
            short = 'P',
            long,
            env = "AYISHA_PID_FILE",
            default_value = "ayisha.pid",
            value_name = "FILE"
        )]
        pid_file: Option<PathBuf>,
    },

    /// Output status and exit.
    Status {
        /// Pid file used by systemctl
        #[arg(
            short = 'P',
            long,
            env = "AYISHA_PID_FILE",
            default_value = "ayisha.pid",
            value_name = "FILE"
        )]
        pid_file: Option<PathBuf>,
    },

    ///
    Shutdown {
        /// Only read service options from the given file
        #[arg(
            short = 'r',
            long,
            env = "AYISHA_REGISTRY_FILE",
            default_value = "ayisha-registry.toml",
            value_name = "FILE"
        )]
        registry_file: Option<PathBuf>,

        /// Pid file used by systemctl
        #[arg(
            short = 'P',
            long,
            env = "AYISHA_PID_FILE",
            default_value = "ayisha.pid",
            value_name = "FILE"
        )]
        pid_file: Option<PathBuf>,
    },

    /// Output version information and exit.
    Version {},
}

pub fn get_cli() -> Cli {
    Cli::parse()
}

pub fn get_cli_command() -> Option<Commands> {
    CLI.command.clone()
}

// SERVER
pub fn get_server_id() -> usize {
    if let Some(cmd) = get_cli_command() {
        match cmd {
            Commands::Server {
                config_file: _,
                log4rs_file: _,
                server_id,
                server_port: _,
                registry_file: _,
                hba_ident_file: _,
                pid_file: _,
            } => {
                if let Some(server_id) = server_id {
                    return server_id;
                }
            }
            _ => {}
        }
    }

    CONFIG
        .server
        .clone()
        .and_then(|s: ConfigServer| s.server_id)
        .unwrap_or(DEFAULT_SERVER_ID)
}

pub fn get_server_port() -> u16 {
    if let Some(cmd) = get_cli_command() {
        match cmd {
            Commands::Server {
                config_file: _,
                log4rs_file: _,
                server_id: _,
                server_port,
                registry_file: _,
                hba_ident_file: _,
                pid_file: _,
            } => {
                if let Some(server_port) = server_port {
                    return server_port;
                }
            }
            _ => {}
        }
    }
    CONFIG
        .server
        .clone()
        .and_then(|s: ConfigServer| s.port)
        .unwrap_or(DEFAULT_PORT)
}

// 获取本机IP
// pub fn get_local_ip() -> Vec<&'static str> {
//     let ips = vec!["localhost", "127.0.0.1"];

//     println!("local_ip::{:?}", local_ip_address::local_ip());

//     ips
// }

pub fn get_server_worker() -> usize {
    CONFIG
        .server
        .clone()
        .and_then(|s: ConfigServer| s.worker)
        .unwrap_or(DEFAULT_WORKER)
}

pub fn get_server_heartbeat() -> ConfigHeartbeat {
    CONFIG
        .server
        .clone()
        .and_then(|s: ConfigServer| s.heartbeat)
        .unwrap_or(ConfigHeartbeat {
            interval: Some(DEFAULT_SERVER_HEARTBEAT_INTERVAL),
            timeout: Some(DEFAULT_SERVER_HEARTBEAT_TIMEOUT),
        })
}

// pub fn get_server_discovery_wait_timeout() -> usize {
//     CONFIG
//         .server
//         .clone()
//         .and_then(|s: ConfigServer| s.discovery_wait_timeout)
//         .unwrap_or(60)
// }

// pub fn get_server_weight() -> usize {
//     let opt = Opt::from_args();
//     if let Some(server_weight) = opt.server_weight {
//         return server_weight;
//     }
//     CONFIG
//         .server
//         .clone()
//         .and_then(|s: ConfigServer| s.weight)
//         .unwrap_or(1)
// }

pub fn get_server_auto_switchover() -> bool {
    CONFIG
        .server
        .clone()
        .and_then(|s| s.auto_switchover)
        .unwrap_or(false)
}

pub fn get_server_singleton() -> bool {
    CONFIG
        .server
        .clone()
        .and_then(|s| s.singleton)
        .unwrap_or(false)
}

pub fn get_server_data_root() -> PathBuf {
    let pwd = env::current_dir().unwrap();
    let default_data_root = pwd.join(format!(".{}", env!("CARGO_PKG_NAME")));
    let data_root = CONFIG
        .server
        .clone()
        .and_then(|s| s.data_root)
        .unwrap_or(default_data_root.display().to_string());

    let path = Path::new(&data_root).to_path_buf();
    fs::create_dir_all(&path).unwrap();
    path
}

#[allow(dead_code)]
pub fn get_server_root() -> PathBuf {
    let path = get_server_data_root().join(format!("{}", get_server_id()));
    fs::create_dir_all(&path).unwrap();
    path
}

// const DEFAULT_VALUE: usize = 3;
// pub fn get_server_broadcast_interval_secs(srv_name: &str, namespace: &str) -> usize {
//     let config_broadcast = get_server().and_then(|s|s.broadcast).unwrap_or_default();

//     match config_broadcast.get(srv_name) {
//         Some(data) => {
//             match data.get(namespace) {
//                 Some(en) => {
//                     en.interval_secs.unwrap_or(DEFAULT_VALUE)
//                 }
//                 None => DEFAULT_VALUE
//             }
//         },
//         None => DEFAULT_VALUE
//     }
// }

// pub fn get_server_broadcast_entry(srv_name: &str, namespace: &str) -> Option<ConfigBroadcastEntry> {
//     let config_broadcast = get_server().and_then(|s| s.broadcast).unwrap_or_default();
//     match config_broadcast.get(srv_name) {
//         Some(data) => match data.get(namespace) {
//             Some(en) => {
//                 return Some(en.to_owned());
//             }
//             _ => {}
//         },
//         _ => {}
//     }
//     None
// }

pub fn get_server_pid_file() -> PathBuf {
    if let Some(cmd) = get_cli_command() {
        match cmd {
            Commands::Server {
                config_file: _,
                log4rs_file: _,
                server_id: _,
                server_port: _,
                registry_file: _,
                hba_ident_file: _,
                pid_file,
            } => pid_file.unwrap_or(PathBuf::from(DEFAULT_PID_FILE)),
            _ => PathBuf::from(DEFAULT_PID_FILE),
        }
    } else {
        PathBuf::from(DEFAULT_PID_FILE)
    }
}

pub fn get_members() -> Vec<ConfigNode> {
    let members = match CONFIG.member.clone() {
        Some(members) => {
            let mut arr = Vec::new();
            let mut server_ids = vec![];
            let mut exists = false;
            for (_, v) in members {
                if v.id == get_server_id() {
                    if let Some(port) = v.port {
                        if port != get_server_port() {
                            panic!("fatal error: server id {}: server port {} with member port {} do not match", v.id, get_server_port(), port);
                        }
                    }
                    // 判断本机IP
                    let host = match v.clone().host {
                        Some(h) => h,
                        None => String::from("127.0.0.1")
                    };
                    if let Ok(network_interfaces) = local_ip_address::list_afinet_netifas() {
                        let mut contains = false;
                        for (_name, ip) in network_interfaces.iter() {
                            if host == ip.to_string() {
                                contains = true;
                                break;
                            }
                        }
                        if !contains {
                            let mut list = vec![];
                            for (name, ip) in network_interfaces.iter() {
                                list.push(format!("{}:\t{:?}", name, ip));
                            }
                            panic!("fatal error: member host {} not in the available network interfaces list: \n{}", host, list.join("\n"));
                        }
                    }
                    exists = true;
                }
                server_ids.push(v.id);
                arr.push(v);
            }
            // serverid是否在members列表里
            if !exists {
                panic!("fatal error: server id {} not in members {:?}", get_server_id(), server_ids);
            }
            // 
            arr
        }
        None => Vec::new(),
    };
    // let nodes = CONFIG.member.clone().unwrap_or(Vec::new());
    // // assert!(nodes.len() < 3, "[[node]] At least 3 nodes");
    // if nodes.len() < 3 {
    //     panic!("[[node]] At least 3 nodes");
    // }
    // nodes
    members
}

pub fn get_node_byid(id: usize) -> Option<ConfigNode> {
    for node in get_members().iter() {
        if node.id == id {
            return Some(node.to_owned());
        }
    }
    None
}

pub fn get_my_weight() -> usize {
    match get_node_byid(get_server_id()) {
        Some(node) => node.weight.unwrap_or_default(),
        None => 0,
    }
}

// CLIENT

pub fn get_client_app_key() -> String {
    CONFIG
        .client
        .clone()
        .and_then(|c| c.app_key)
        .unwrap_or_default()
}

pub fn get_client_app_secret() -> String {
    CONFIG
        .client
        .clone()
        .and_then(|c| c.app_secret)
        .unwrap_or_default()
}

#[allow(dead_code)]
pub fn get_client_connect_retry_wait() -> usize {
    let value = CONFIG
        .client
        .clone()
        .and_then(|c| c.connect_retry_wait)
        .unwrap_or(DEFAULT_CLIENT_CONNECT_RETRY_WAIT);

    if value >= MIN_CLIENT_CONNECT_RETRY_WAIT && value <= MAX_CLIENT_CONNECT_RETRY_WAIT {
        value
    } else {
        DEFAULT_CLIENT_CONNECT_RETRY_WAIT
    }
}

#[allow(dead_code)]
pub fn get_server() -> Option<ConfigServer> {
    CONFIG.server.clone()
}

pub fn get_config() -> Config {
    let mut config = Config::new();

    if let Some(cmd) = get_cli_command() {
        match cmd {
            Commands::Server {
                config_file,
                log4rs_file: _,
                server_id: _,
                server_port: _,
                registry_file: _,
                hba_ident_file: _,
                pid_file: _,
            } => {
                if let Some(config_file) = config_file {
                    if !config_file.exists() {
                        // 文件不存在
                        panic!("--config-file: {}: No such file", config_file.display());
                    }
                    let toml_text = fs::read_to_string(&config_file).unwrap();
                    let c: Config = match toml::from_str(&toml_text) {
                        Ok(p) => p,
                        Err(e) => {
                            panic!(
                                "<ParseTOML> File {} cannot be parsed, cause:\n{}",
                                config_file.display(),
                                e
                            );
                        }
                    };

                    config.clone_from(&c)
                }
            }
            _ => {}
        }
    }

    // log::info!("{} {}", "#".repeat(3), ">".repeat(76));
    // log::info!(
    //     "### {}\n{}",
    //     &args.config_file,
    //     toml::to_string_pretty(&config).unwrap()
    // );
    // log::info!("{} {}", "#".repeat(3), "<".repeat(76));

    config
}

pub fn get_registry() -> Registry {
    let mut registry: Registry = Registry::new();

    if let Some(cmd) = get_cli_command() {
        match cmd {
            Commands::Server {
                config_file: _,
                log4rs_file: _,
                server_id: _,
                server_port: _,
                registry_file,
                hba_ident_file: _,
                pid_file: _,
            } => {
                if let Some(registry_file) = registry_file {
                    let c = get_raw_registry(registry_file);
                    registry.clone_from(&c)
                }
            }
            _ => {}
        }
    }

    // let toml_text = fs::read_to_string(&args.registry_file).unwrap();
    // let registry: Registry = match toml::from_str(&toml_text) {
    //     Ok(p) => p,
    //     Err(e) => {
    //         panic!(
    //             "<ParseTOML> File {} cannot be parsed, cause:\n{}",
    //             &args.registry_file, e
    //         );
    //     }
    // };

    // log::info!("{} {}", "#".repeat(3), ">".repeat(76));
    // log::info!(
    //     "### {}\n{}",
    //     &args.registry_file,
    //     toml::to_string_pretty(&registry).unwrap()
    // );
    // log::info!("{} {}", "#".repeat(3), "<".repeat(76));

    registry
}

pub fn get_raw_registry(registry_file: PathBuf) -> Registry {
    if !registry_file.exists() {
        // 文件不存在
        panic!("--registry-file: {}: No such file", registry_file.display());
    }
    let toml_text = fs::read_to_string(&registry_file).unwrap();
    let c: Registry = match toml::from_str(&toml_text) {
        Ok(p) => p,
        Err(e) => {
            panic!(
                "<ParseTOML> File {} cannot be parsed, cause:\n{}",
                registry_file.display(),
                e
            );
        }
    };

    c
}

pub fn get_registry_services() -> HashMap<String, RegistryServiceEntry> {
    REGISTRY.services.clone()
}

pub fn get_registry_env() -> Option<HashMap<String, String>> {
    REGISTRY.env.clone()
}

// fn parse_file_path(p: &str) -> Result<String, io::Error> {
//     match File::open(p) {
//         Ok(_) => Ok(String::from(p)),
//         Err(e) => Err(Error::new(io::ErrorKind::NotFound, format!("{}: {}", p, e))),
//     }
// }

pub fn get_encoding(encoding_str: Option<String>) -> &'static Encoding {
    match encoding_str {
        None => UTF_8,
        Some(label) => match Encoding::for_label((&label).as_bytes()) {
            None => {
                panic!("{} is not a known encoding label; exiting.", label);
            }
            Some(encoding) => encoding,
        },
    }
}

#[cfg(test)]
mod tests {
    use std::fs;

    use super::Registry;

    #[test]
    fn test_registry() {
        let f = "/Users/lbk/ayisha/ayisha-registry2.toml";
        let toml_text = fs::read_to_string(&f).unwrap();
        let registry: Registry = match toml::from_str(&toml_text) {
            Ok(p) => p,
            Err(e) => {
                panic!("<ParseTOML> File {} cannot be parsed, cause:\n{}", f, e);
            }
        };

        println!("{:?}", registry);
    }

    #[test]
    fn test_uri_ayfile() {
        // let url = "file::///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        let url = "ayisha://192.168.1.1,192.168.1.2,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            // match url::Url::parse("ayisha://192.168.1.1,192.168.1.2,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            // match url::Url::parse("file:///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            // match actix_http::Uri::from_str("ayfile://192.168.1.1:6969,192.168.1.2:6969,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            Ok(url) => {
                match url.scheme() {
                    "file" => {
                        // local path
                    }
                    "ayisha" => {
                        // 远程目录
                    }
                    _ => {}
                }
                // assert_eq!(url.scheme(), "");
                // assert_eq!(url.scheme(), "file");
                assert_eq!(url.scheme(), "ayisha");
                assert_eq!(url.host_str(), Some("192.168.1.1,192.168.1.2,192.168.1.3"));
                assert_eq!(url.port(), Some(6969));
                assert_eq!(
                    url.path(),
                    "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties"
                );
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[test]
    fn test_uri_ayfile_noport() {
        // let url = "file::///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        let url = "ayisha://192.168.1.1,192.168.1.2,192.168.1.3/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            // match url::Url::parse("ayisha://192.168.1.1,192.168.1.2,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            // match url::Url::parse("file:///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            // match actix_http::Uri::from_str("ayfile://192.168.1.1:6969,192.168.1.2:6969,192.168.1.3:6969/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties") {
            Ok(url) => {
                match url.scheme() {
                    "file" => {
                        // local path
                    }
                    "ayisha" => {
                        // 远程目录
                    }
                    _ => {}
                }
                // assert_eq!(url.scheme(), "");
                // assert_eq!(url.scheme(), "file");
                assert_eq!(url.scheme(), "ayisha");
                assert_eq!(url.host_str(), Some("192.168.1.1,192.168.1.2,192.168.1.3"));
                assert_eq!(url.port(), None);
                assert_eq!(
                    url.path(),
                    "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties"
                );
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[test]
    fn test_uri_file() {
        let url = "file:///data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            Ok(url) => {
                assert_eq!(url.scheme(), "file");
                assert_eq!(url.host_str(), None);
                assert_eq!(url.port(), None);
                assert_eq!(
                    url.path(),
                    "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties"
                );
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[test]
    fn test_uri_file2() {
        let url = "file://data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            Ok(url) => {
                match url.scheme() {
                    "file" => {
                        if url.host_str().is_some() {
                            println!("Path is missing `/`, Path={}", url.path())
                        }
                        // 本地目录
                        assert_eq!(url.host_str(), Some("data"));
                        assert_eq!(url.port(), None);
                        assert_eq!(
                            url.path(),
                            "/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties"
                        );
                    }
                    "ayisha" => {
                        // 远程目录
                        assert_eq!(url.host_str(), Some("192.168.1.1,192.168.1.2,192.168.1.3"));
                        assert_eq!(url.port(), None);
                        assert_eq!(
                            url.path(),
                            "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties"
                        );
                    }
                    _ => {}
                }
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[test]
    fn test_uri_local() {
        let url = "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            Ok(url) => {
                match url.scheme() {
                    "file" => {
                        if url.host_str().is_some() {
                            println!("Path is missing `/`, Path={}", url.path())
                        }
                    }
                    _ => {}
                }

                assert_eq!(url.scheme(), "file");
                assert_eq!(url.host_str(), Some("192.168.1.1,192.168.1.2,192.168.1.3"));
                assert_eq!(url.port(), Some(6969));
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[test]
    fn test_uri() {
        let url = "/data/dataxone/sync/srv01/dt_srv01/.ayisha/0.properties";
        match url::Url::parse(url) {
            Ok(url) => {
                assert_eq!(url.scheme(), "file");
                assert_eq!(url.host_str(), Some("192.168.1.1,192.168.1.2,192.168.1.3"));
                assert_eq!(url.port(), Some(6969));
            }
            Err(url::ParseError::RelativeUrlWithoutBase) => {
                // 本地路径
                println!("local_path: {url}");
            }
            Err(_) => {
                return;
            }
        }
    }


}
